/*
 * Copyright (c) Meta Platforms, Inc. and affiliates.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */

package com.facebook.marianatrench.integrationtests;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;

class ExportedActivity extends Activity {
  private Object mSource;

  @Override
  protected void onCreate() {
    mSource = Origin.source();
    ExploitabilityRule.testExported(this);

    // Call so that both Exported and ExportedDfa are reachable where
    // distance(Exported) > distance(ExportedDfa)
    onDfaExported();
  }

  @Override
  protected void onStart() {
    // Issues reported onStart().
    Origin.sink(Origin.source());
    ExploitabilityRule.testMultihopExported(this);

    // The taint flow issue is only found on the lifecycle wrapper.
    // We need to mark the lifecycle wrapper as Exported to
    // find the exploitability issue.
    Origin.sink(mSource);
  }

  @Override
  protected void onCreate(Bundle bundle) {
    // Note: This method is modelled with 2 effect sources: Exported and
    // ExportedDfa.
    ExploitabilityRule.testMultipleEffectSourcesSameDistance(this);
  }

  protected void onDfaExported() {
    // Note: This method is modelled with effect source ExportedDfa only,
    // but is called from onCreate() which is an Exported
    ExploitabilityRule.testMultipleEffectSourcesDifferentDistance(this);
  }

  protected Intent getIntentHop1() {
    return getIntentHop2();
  }

  protected Intent getIntentHop2() {
    return this.getIntent();
  }

  protected void hopToIssue() {
    ExploitabilityRule.testExported(this);
  }

  protected void testMultiplePathsToExploitabilityRoot() {
    // This method is Exported _and_ has call-chain flows to the same exploitability-root via
    // multiple paths.
    // Expect: a single issue here.

    // Path 1: Direct path to issue.
    ExploitabilityRule.testExported(this);

    // Path 2: Hop to issue.
    hopToIssue();
  }

  protected void testExportedParameterSource() {
    ExploitabilityRule.testExportedParameterSource(/* untainted */ new Intent());

    // Expect issue: Source/Sink flow here with Source.
    ExploitabilityRule.testExportedParameterSource(/* tainted */ getIntent());
  }
}

class UnexportedActivity extends Activity {
  private Object mSource;

  @Override
  protected void onCreate() {
    mSource = Origin.source();
    ExploitabilityRule.testUnexported(this);
  }

  @Override
  protected void onStart() {
    Origin.sink(mSource);
  }
}

public class ExploitabilityRule {
  static void dfaSink(Object data) {}

  static void hopToSink(Object data) {
    Origin.sink(data);
  }

  static void testExported(Activity activity) {
    // Expect issue: Source/Sink flow here + testExported() is "exported" via
    // ExportedActivity::onCreate()
    Origin.sink(activity.getIntent());
  }

  static void testUnexported(Activity activity) {
    // Expect no issue: Source/Sink flow here. But testUnexported()
    // called from UnexportedActivitiy::onCreate() is not "exported"
    Origin.sink(activity.getIntent());
  }

  static void testMultihopExported(ExportedActivity activity) {
    // Expect issue: Source/Sink flow here + testExported() is "exported" via
    // ExportedActivity::onCreate()
    hopToSink(activity.getIntentHop1());
  }

  static void testMultipleEffectSourcesSameDistance(Activity activity) {
    // Expect issue: Source/Sink flow here + testMultipleEffectSourcesSameDistance() is "exported"
    // via ExportedActivity::onCreate(Bundle) which has both Exported and
    // ExportedDfa. Expect both as sources.
    dfaSink(activity.getIntent());
  }

  static void testMultipleEffectSourcesDifferentDistance(Activity activity) {
    // Expect issue: Source/Sink flow here + testMultipleEffectSourcesDifferentDistance() is
    // ExportedDfa via ExportedActivity::onDfaExported(Bundle) but also is reachable from onCreate()
    // which is Exported. But expect issue for only the shortest distance, which is ExportedDfa.
    dfaSink(activity.getIntent());
  }

  static void testExportedParameterSource(Intent intent) {
    // Expect issue: Source/Sink flow here with ParameterSource.
    // Exported via ExportedActivity::testExportedParamaterSource().
    hopToSink(intent);
  }
}
